package io.gitee.xjt2016.modules.gen.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ClassUtil;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLDataType;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement;
import com.alibaba.druid.util.JdbcConstants;
import com.alibaba.fastjson.JSON;
import com.google.common.base.CaseFormat;
import com.google.common.base.Converter;
import com.google.common.base.Preconditions;
import com.mysql.cj.MysqlType;
import io.gitee.xjt2016.modules.api.extension.Commons;
import io.gitee.xjt2016.modules.common.utils.ValidatorUtils;
import io.gitee.xjt2016.modules.gen.domain.GenSchema;
import io.gitee.xjt2016.modules.gen.domain.GenTable;
import io.gitee.xjt2016.modules.gen.domain.GenTableColumn;
import jetbrick.template.JetEngine;
import jetbrick.template.JetTemplate;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.io.StringWriter;
import java.sql.JDBCType;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Log4j2
public class GenUtil {
    // 驼峰转下划线, userName -> user_name
    private static final Converter<String, String> converter = CaseFormat.LOWER_UNDERSCORE.converterTo(CaseFormat.LOWER_CAMEL);
    private static final Converter<String, String> converter2 = CaseFormat.LOWER_UNDERSCORE.converterTo(CaseFormat.LOWER_CAMEL);

    // 1. 创建一个默认的 JetEngine
    public static final JetEngine engine = JetEngine.create();

    static {
        engine.getGlobalResolver().registerFunctions(Commons.class);
    }

    public static GenTable sql2GenTable(@NonNull String sql) {
        GenTable genTable = new GenTable();

        final String dbType = JdbcConstants.MYSQL.toString(); // 可以是ORACLE、POSTGRESQL、SQLSERVER、ODPS等
        List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);

        Preconditions.checkArgument(stmtList != null && stmtList.size() == 1, "单条创建语句");
        SQLStatement sqlStatement = stmtList.get(0);
        Preconditions.checkArgument(sqlStatement instanceof MySqlCreateTableStatement, "不是mysql创建语句");
        MySqlCreateTableStatement mySqlCreateTableStatement = (MySqlCreateTableStatement) sqlStatement;

        String tableName = mySqlCreateTableStatement.getName().getSimpleName();
        tableName = tableName.replaceAll("`", "");
        genTable.setTableName(tableName);
        genTable.setEntityName(converter2.convert(tableName));

        List<GenTableColumn> columns = new ArrayList<>();
        mySqlCreateTableStatement.forEachColumn(sqlColumnDefinition -> {
            GenTableColumn genTableColumn = new GenTableColumn();

            String colName = sqlColumnDefinition.getNameAsString().replaceAll("`", "");
            genTableColumn.setColName(colName);
            genTableColumn.setJavaFieldName(converter.convert(colName));

            String colComment = sqlColumnDefinition.getComment() != null ? sqlColumnDefinition.getComment().toString() : "";
            genTableColumn.setColComment(colComment.replaceAll("'", ""));

            SQLDataType dataType = sqlColumnDefinition.getDataType();

            String colType = StringUtils.lowerCase(dataType.getName());
            MysqlType mysqlType = MysqlType.valueOf(colType.toUpperCase());

            JDBCType jdbcType = JDBCType.valueOf(mysqlType.getJdbcType());

            genTableColumn.setColType(jdbcType.getName().toLowerCase());

            genTableColumn.setPrecision(mysqlType.getPrecision());
//            genTableColumn.setColSqlType(mysqlType.getJdbcType());

            genTableColumn.setJavaFieldType(ClassUtil.loadClass(mysqlType.getClassName()).getSimpleName());

            columns.add(genTableColumn);
        });

        //列名排序
        genTable.setColumns(columns
                .stream()
                .sorted(Comparator.comparing(GenTableColumn::getColName))
                .collect(Collectors.toList())
        );
        return genTable;
    }


    public static void main(String[] args) throws Exception {
        String sql = "CREATE TABLE `rds_order` (\n" +
                "  `oid` int NOT NULL,\n" +
                "  `tid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `seller_id` bigint DEFAULT NULL,\n" +
                "  `created` timestamp NULL DEFAULT NULL,\n" +
                "  `modified` timestamp NULL DEFAULT NULL,\n" +
                "  `huohao` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `shichang` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `dangkou` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `cenben` varchar(21) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `pic_path` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `refund_status` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `status` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `title` varchar(127) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `outer_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `item_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `sku_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `outer_sku_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `number` int DEFAULT NULL,\n" +
                "  `price` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `total_fee` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `discount_fee` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `adjust_fee` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `payment` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `waybill_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `waybill_code` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `waybill_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  `end_time` timestamp NULL DEFAULT NULL,\n" +
                "  `consign_time` timestamp NULL DEFAULT NULL,\n" +
                "  `sku_properties` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,\n" +
                "  PRIMARY KEY (`oid`),\n" +
                "  KEY `idx_seller_id_tid` (`tid`,`seller_id`),\n" +
                "  KEY `idx_created` (`created`)\n" +
                ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;";
        GenTable genTable = sql2GenTable(sql);

        GenSchema genSchema = new GenSchema(genTable);

        genSchema.setModuleName("demo");
        genSchema.setBasePackageName("io.gitee.xjt2016.modules");
        genSchema.setEntityName("Demo");
        genSchema.setGenPath("/Users/xjt2016/Documents/gitee/awesome-all/awesome/awesome-api/src/test/java/");

        genByGenTable(genSchema);
    }

    public static void genByGenTable(GenSchema genSchema) throws Exception {

        String baseDist = ClassUtil.getClassPath() + "gen/";
        baseDist = genSchema.getGenPath();

//        baseDist += File.separator + genSchema.getBasePackageName().replaceAll("\\.", "/");
//        baseDist += File.separator + genSchema.getModuleName();
        log.debug(baseDist);

        ValidatorUtils.validateWithException(genSchema);

        Preconditions.checkNotNull(genSchema.getModuleName());
        Preconditions.checkNotNull(genSchema.getBasePackageName());
        Preconditions.checkNotNull(genSchema.getEntityName());

        String baseTemPath = "gen/templates";

        String mybatisPlusTmpPath = baseTemPath + "/mybatisplus";

        // 3. 创建 context 对象
        final Map<String, Object> context = JSON.parseObject(JSON.toJSONString(genSchema), Map.class);

        List<String> colNameList = new ArrayList<>();
        List<String> javaNameList = new ArrayList<>();
        for (GenTableColumn column : genSchema.getGenTable().getColumns()) {
            colNameList.add("`" + column.getColName() + "`");
            javaNameList.add("#{item." + column.getJavaFieldName() + "}");
        }
        System.out.println(StringUtils.join(colNameList, ","));
        System.out.println(StringUtils.join(javaNameList, ","));

        context.put("packageName", genSchema.getPackageName("domain"));
        String domainPath = baseDist + File.separator + genSchema.getPackageName("domain").replaceAll("\\.", "/");
        //entity
        String entity = render(context, mybatisPlusTmpPath + "/entity.jetx");
        log.debug("render entity:\n{}", entity);
        FileUtil.writeUtf8String(entity, domainPath + File.separator + genSchema.getEntityName() + ".java");

        context.put("packageName", genSchema.getPackageName("vo"));
        String voPath = baseDist + File.separator + genSchema.getPackageName("vo").replaceAll("\\.", "/");
        //entity
        String vo = render(context, mybatisPlusTmpPath + "/vo.jetx");
        log.debug("render entity:\n{}", entity);
        FileUtil.writeUtf8String(vo, voPath + File.separator + genSchema.getEntityName() + "Vo.java");

        context.put("packageName", genSchema.getPackageName("mapper"));
        String mapperPath = baseDist + File.separator + genSchema.getPackageName("mapperx").replaceAll("\\.", "/");
        //dao
        String mapper = render(context, mybatisPlusTmpPath + "/mapper.jetx");
        log.debug("render mapper:\n{}", mapper);
        FileUtil.writeUtf8String(mapper, mapperPath + File.separator + genSchema.getEntityName() + "Mapper.java");

        context.put("packageName", genSchema.getPackageName("service"));
        String servicePath = baseDist + File.separator + genSchema.getPackageName("service").replaceAll("\\.", "/");
        //service
        String service = render(context, mybatisPlusTmpPath + "/service.jetx");
        FileUtil.writeUtf8String(service, servicePath + File.separator + genSchema.getEntityName() + "Service.java");

        context.put("packageName", genSchema.getPackageName("controller"));
        String controllerPath = baseDist + File.separator + genSchema.getPackageName("controller").replaceAll("\\.", "/");
        //controller
        String controller = render(context, mybatisPlusTmpPath + "/controller.jetx");
        FileUtil.writeUtf8String(controller, controllerPath + File.separator + genSchema.getEntityName() + "Controller.java");
    }

    public static String render(Map<String, Object> context, String templatePath) {

        // 2. 获取一个模板对象 (从默认的 classpath 下面)
        JetTemplate template = engine.getTemplate(templatePath);

        // 4. 渲染模板到自定义的 Writer
        StringWriter writer = new StringWriter();
        template.render(context, writer);

        // 5. 打印结果
        return writer.toString();
    }
}
